package com.alibaba.otter.canal.filter;

import com.alibaba.otter.canal.filter.exception.CanalFilterException;

/**
 * 数据过滤机制
 *
 * filter模块用于对binlog进行过滤。
 * 在实际开发中，一个mysql实例中可能会有多个库，每个库里面又会有多个表，
 * 如果只是想订阅某个库中的部分表，这个时候就需要进行过滤。
 * 也就是说，parser模块解析出来binlog之后，会进行一次过滤之后，才会存储到store模块中。
 * 过滤规则的配置既可以在canal服务端进行，也可以在客户端进行。
 *
 * 服务端配置
 * instance.properties中有以下两个配置项：
 * canal.instance.filter.regex用于配置白名单，也即希望订阅哪些库，哪些表，
 *   默认值为.*\\..*，也就是订阅所有库，所有表。
 * canal.instance.filter.black.regex用于配置黑名单，也即不希望订阅哪些库，哪些表。
 *   没有默认值，也就是默认黑名单为空。
 * 需要注意的是，在过滤的时候，会先根据白名单进行过滤，再根据黑名单过滤。
 * 意味着，如果一张表在白名单和黑名单中都出现了，那么这张表最终不会被订阅到，
 * 因为白名单通过后，黑名单又将这张表给过滤掉了。
 * 另外一点值得注意的是，过滤规则使用的是perl正则表达式，而不是jdk自带的正则表达式。
 * 意味着filter模块引入了其他依赖，来进行匹配。具体来说，filter模块的pom.xml中包含以下两个依赖：
 * aviator：是一个开源的、高性能、轻量级的 java 语言实现的表达式求值引擎
 * oro：全称为Jakarta ORO，最全面以及优化得最好的正则表达式API之一，
 *   Jakarta-ORO库以前叫做OROMatcher，是由DanielF. Savarese编写，后来捐赠给了apache Jakarta Project。
 *   canal的过滤规则就是通过oro中的Perl5Matcher来进行完成的。
 * 显然，对于filter模块的源码解析，实际上主要变成了对aviator、oro的分析。
 * 这一点可以从filter模块核心接口CanalEventFilter的实现类中得到验证。
 * CanalEventFilter接口定义了一个filter方法。
 *
 * 目前针对CanalEventFilter提供了3个实现类，都是基于开源的java表达式求值引擎Aviator：
 * 这个3个实现都是以Aviater开头，应该是拼写错误，正确的应该是Aviator。
 * AviaterELFilter：基于Aviator el表达式的匹配过滤
 * AviaterSimpleFilter：基于Aviator进行tableName简单过滤计算，不支持正则匹配
 * AviaterRegexFilter：基于Aviator进行tableName正则匹配的过滤算法。
 *   内部使用到了一个RegexFunction类，这是对Aviator自定义的函数的扩展，内部使用到了oro中的Perl5Matcher来进行正则匹配。
 *
 * 尽管filter模块提供了3个基于Aviator的过滤器实现，但是实际上使用到的只有AviaterRegexFilter。
 * 在canal-deploy模块提供的xxx-instance.xml配置文件中，
 * 以default-instance.xml为例，eventParser这个bean包含以下两个属性：
 *  eventFilter属性：使用配置项canal.instance.filter.regex的值进行白名单过滤。
 *  eventBlackFilter属性：使用配置项canal.instance.filter.black.regex进行黑名单过滤。
 * 这两个属性的值都是通过一个内部bean的方式进行配置，类型都是AviaterRegexFilter。
 *
 * parser模块在过滤的时候，会先根据canal.instance.filter.regex进行白名单过滤，
 * 再根据 canal.instance.filter.black.regex进行黑名单过滤。
 * 到这里，实际上就是先通过eventFilter进行白名单过滤，通过eventBlackFilter进行黑名单过滤。
 *
 *  parser模块实际上会将eventFilter、eventBlackFilter设置到一个LogEventConvert对象中，
 *  这个对象有2个方法：parseQueryEvent和parseRowsEvent都进行了过滤。
 *  nameFilter、nameBlackFilter实际上就是设置到parser中的 eventFilter、eventBlackFilter，
 *  只不过parser将其设置到LogEventConvert对象中换了一个名字。
 *  可以看到，的确是先使用nameFilter进行白名单过滤，再使用nameBlackFilter进行黑名单过滤。
 *  在过滤时，使用dbName+"."+tableName作为参数，进行过滤。如果被过滤掉了，就返回null。
 *
 * canal也支持客户端配置过滤规则。举例来说，假设一个库有10张表，
 * 一个client希望订阅其中5张表，另一个client希望订阅另5张表。此时，服务端可以订阅10张表，
 * 当client来消费的时候，根据client的过滤规则只返回给对应的binlog event。
 * 客户端指定过滤规则通过client模块中的CanalConnector的subscribe方法来进行，subscribe有两种重载形式
 *
 * 通过不同client指定不同的过滤规则，可以达到服务端一份数据供多个client进行订阅消费的效果。
 * 然而，由于目前一个canal instance只允许一个client订阅，因此还达不到这种效果。
 *
 * @author jianghang 2012-7-20 下午03:51:27
 */
public interface CanalEventFilter<T> {

    boolean filter(T event) throws CanalFilterException;
}
